/**
 * @file    drv_pwm.c
 * @author  yhy
 * @brief   芯必达IM94x系列芯片, pwm抽象层
 * @version 1.01
 * @date    2025-07-14
 *
 * @copyright Copyright (c) 2025
 *
 * @details
 * Change Logs:
 * Date           Author       Notes
 * 2025-07-14     yhy          the first version V1.00
 * 2025-08-04     yhy          新增PWM频率选择 V1.01
 *
 */
#include "drv_pwm.h"

#include "drv_gpio.h"
#include "gpio_drv.h"
#include "ipwm_drv.h"
#include "spwm_drv.h"

#define HASH_TABLE_MAXSIZE (38 + 36) /* hash表大小 */

/* 计算hash table key */
#define CAL_HASH_KEY_SPWM(pin_num, channel, pin)                                                           \
    ((pin_num * pin_num) + (channel * 10) + (pin * 10) + ((channel * 10) + (pin * 10)))

#define CAL_HASH_KEY_IPWM(pin_num, channel, pin)                                                           \
    ((pin_num * pin_num) + (channel * 10) + (pin * 10) + ((channel * 10) + (pin * 10)) + 10000)

/**
 * @brief hash表元素
 *
 */
typedef struct
{
    uint32_t       key;
    gpio_pin_mux_t value;
} snf_hash_entry_t;

/**
 * @brief PWM信息
 *
 */
typedef struct
{
    snf_pwm_instance_e instance;
    snf_pwm_freq_e     freq;
} snf_pwm_info_t;

static result_e       snf_drv_pwm_param_verify(const snf_pwm_config_t *pwm_config);
static void           snf_drv_pwm_gpio_config(const snf_pwm_config_t *pwm_config);
static void           snf_drv_pwm_spwm_config(const snf_pwm_config_t *pwm_config);
static void           snf_drv_pwm_ipwm_config(const snf_pwm_config_t *pwm_config);
static void           snf_drv_pwm_config(const snf_pwm_config_t *pwm_config);
static gpio_pin_mux_t snf_drv_pwm_get_hash_valve(uint32_t hash_key);

/* 端口查找表 */
static GPIO_Type *port_table[] = { GPIOA, GPIOB, GPIOC, GPIOD, GPIOE };

/* 复用功能哈希表 */
static const snf_hash_entry_t mux_hash_table[HASH_TABLE_MAXSIZE] = {
    [0] = { 7249, GPIO_PIN_MUX_FUN2 },   [1] = { 7044, GPIO_PIN_MUX_FUN2 },
    [2] = { 5240, GPIO_PIN_MUX_FUN2 },   [3] = { 5061, GPIO_PIN_MUX_FUN2 },
    [4] = { 6881, GPIO_PIN_MUX_FUN2 },   [5] = { 6680, GPIO_PIN_MUX_FUN2 },
    [6] = { 729, GPIO_PIN_MUX_FUN2 },    [7] = { 644, GPIO_PIN_MUX_FUN2 },
    [8] = { 1641, GPIO_PIN_MUX_FUN2 },   [9] = { 1524, GPIO_PIN_MUX_FUN2 },
    [10] = { 3641, GPIO_PIN_MUX_FUN2 },  [11] = { 256, GPIO_PIN_MUX_FUN2 },
    [12] = { 6461, GPIO_PIN_MUX_FUN2 },  [13] = { 1200, GPIO_PIN_MUX_FUN2 },
    [14] = { 3484, GPIO_PIN_MUX_FUN2 },  [15] = { 209, GPIO_PIN_MUX_FUN2 },
    [16] = { 164, GPIO_PIN_MUX_FUN2 },   [17] = { 6224, GPIO_PIN_MUX_FUN2 },
    [18] = { 5516, GPIO_PIN_MUX_FUN2 },  [19] = { 3145, GPIO_PIN_MUX_FUN7 },
    [20] = { 2996, GPIO_PIN_MUX_FUN7 },  [21] = { 7169, GPIO_PIN_MUX_FUN7 },
    [22] = { 6964, GPIO_PIN_MUX_FUN7 },  [23] = { 6049, GPIO_PIN_MUX_FUN7 },
    [24] = { 5856, GPIO_PIN_MUX_FUN7 },  [25] = { 1529, GPIO_PIN_MUX_FUN7 },
    [26] = { 1416, GPIO_PIN_MUX_FUN7 },  [27] = { 2961, GPIO_PIN_MUX_FUN7 },
    [28] = { 2820, GPIO_PIN_MUX_FUN7 },  [29] = { 521, GPIO_PIN_MUX_FUN7 },
    [30] = { 440, GPIO_PIN_MUX_FUN7 },   [31] = { 6521, GPIO_PIN_MUX_FUN7 },
    [32] = { 1241, GPIO_PIN_MUX_FUN7 },  [33] = { 1140, GPIO_PIN_MUX_FUN7 },
    [34] = { 169, GPIO_PIN_MUX_FUN7 },   [35] = { 124, GPIO_PIN_MUX_FUN7 },
    [36] = { 6344, GPIO_PIN_MUX_FUN7 },  [37] = { 5636, GPIO_PIN_MUX_FUN7 },
    [38] = { 13105, GPIO_PIN_MUX_FUN2 }, [39] = { 12956, GPIO_PIN_MUX_FUN2 },
    [40] = { 16089, GPIO_PIN_MUX_FUN2 }, [41] = { 14528, GPIO_PIN_MUX_FUN2 },
    [42] = { 11429, GPIO_PIN_MUX_FUN2 }, [43] = { 11356, GPIO_PIN_MUX_FUN2 },
    [44] = { 12961, GPIO_PIN_MUX_FUN2 }, [45] = { 12820, GPIO_PIN_MUX_FUN2 },
    [46] = { 10521, GPIO_PIN_MUX_FUN2 }, [47] = { 10440, GPIO_PIN_MUX_FUN2 },
    [48] = { 10441, GPIO_PIN_MUX_FUN2 }, [49] = { 10364, GPIO_PIN_MUX_FUN2 },
    [50] = { 12225, GPIO_PIN_MUX_FUN2 }, [51] = { 12096, GPIO_PIN_MUX_FUN2 },
    [52] = { 11281, GPIO_PIN_MUX_FUN2 }, [53] = { 10081, GPIO_PIN_MUX_FUN2 },
    [54] = { 10040, GPIO_PIN_MUX_FUN2 }, [55] = { 12049, GPIO_PIN_MUX_FUN2 },
    [56] = { 11924, GPIO_PIN_MUX_FUN2 }, [57] = { 10449, GPIO_PIN_MUX_FUN2 },
    [58] = { 10384, GPIO_PIN_MUX_FUN2 }, [59] = { 10361, GPIO_PIN_MUX_FUN2 },
    [60] = { 10300, GPIO_PIN_MUX_FUN2 }, [61] = { 11821, GPIO_PIN_MUX_FUN2 },
    [62] = { 16029, GPIO_PIN_MUX_FUN3 }, [63] = { 15876, GPIO_PIN_MUX_FUN3 },
    [64] = { 13309, GPIO_PIN_MUX_FUN4 }, [65] = { 13196, GPIO_PIN_MUX_FUN4 },
    [66] = { 10501, GPIO_PIN_MUX_FUN6 }, [67] = { 10460, GPIO_PIN_MUX_FUN6 },
    [68] = { 10021, GPIO_PIN_MUX_FUN6 }, [69] = { 10020, GPIO_PIN_MUX_FUN6 },
    [70] = { 11989, GPIO_PIN_MUX_FUN6 }, [71] = { 11904, GPIO_PIN_MUX_FUN6 },
    [72] = { 10429, GPIO_PIN_MUX_FUN7 }, [73] = { 10404, GPIO_PIN_MUX_FUN7 },
};

/* PWM信息 */
static snf_pwm_info_t pwm_info[PWM_INSTANCE_MAX] = {
    [PWM_INSTANCE_SPWM0] = { PWM_INSTANCE_SPWM0, PWM_FREQ_20KHZ },
    [PWM_INSTANCE_SPWM1] = { PWM_INSTANCE_SPWM1, PWM_FREQ_20KHZ },
    [PWM_INSTANCE_SPWM2] = { PWM_INSTANCE_SPWM2, PWM_FREQ_20KHZ },
    [PWM_INSTANCE_IPWM0] = { PWM_INSTANCE_IPWM0, PWM_FREQ_20KHZ },
    [PWM_INSTANCE_IPWM1] = { PWM_INSTANCE_IPWM1, PWM_FREQ_20KHZ },
};

/**
 * @brief 初始化PWM, 默认频率为20kHz
 *
 * @param pwm_config     PWM配置结构体
 * @return result_e      初始化结果, RESULT_STATUS_OK表示成功, 其他值表示失败
 */
result_e snf_drv_pwm_init(const snf_pwm_config_t *pwm_config)
{
    result_e result = snf_drv_pwm_param_verify(pwm_config);
    if (result != RESULT_STATUS_OK) {
        return result;
    }

    snf_drv_pwm_gpio_config(pwm_config);
    snf_drv_pwm_config(pwm_config);

    pwm_info[pwm_config->instance].freq = pwm_config->freq;

    return RESULT_STATUS_OK;
}

/**
 * @brief 设置PWM占空比
 *
 * @param instance      PWM实例
 * @param channel       PWM通道号
 * @param duty          占空比
 * @return result_e     设置结果, RESULT_STATUS_OK为成功, 其他为失败
 */
result_e snf_drv_pwm_set_duty(snf_pwm_instance_e instance, snf_pwm_channel_e channel, uint8_t duty)
{
    if (instance >= PWM_INSTANCE_MAX) {
        return RESULT_STATUS_INVALID_PARAM;
    }

    if (duty > 100) {
        duty = 100;
    }

    if (instance >= PWM_INSTANCE_IPWM0) {
        uint16_t duty_value;
        uint16_t period_value;

        if (pwm_info[instance].freq == PWM_FREQ_100HZ) {
            period_value = 25000U - 1U; // 与配置中的mod_value一致
            if (duty == 0) {
                duty_value = period_value;
            }
            else if (duty == 100) {
                duty_value = 0; // 100%占空比时使用period_value
            }
            else {
                duty_value = (uint16_t)((duty * (uint32_t)period_value) / 100UL);
            }
        }
        else {
            period_value = 100U - 1U;
            if (duty == 0) {
                duty_value = period_value;
            }
            else if (duty == 100) {
                duty_value = 0; // 100%占空比时使用period_value
            }
            else {
                duty_value = duty;
            }
        }

        // 移除强制设置为1的逻辑，允许真正的0%占空比
        IPWM_SetChannelValue(instance - 3, (ipwm_channel_t)channel, period_value, duty_value);
    }
    else {
        uint16_t duty_value;

        if (pwm_info[instance].freq == PWM_FREQ_100HZ) {
            // SPWM 100Hz时需要按比例转换
            duty_value = (uint16_t)((duty * 9999UL) / 100UL);
        }
        else {
            duty_value = duty;
        }

        SPWM_SetChannelValue(instance, (spwm_channel_t)channel, duty_value);
    }

    return RESULT_STATUS_OK;
}

/**
 * @brief 配置PWM参数
 *
 * @param pwm_config pwm配置结构体指针
 */
static void snf_drv_pwm_config(const snf_pwm_config_t *pwm_config)
{
    if (pwm_config->instance >= PWM_INSTANCE_IPWM0) {
        snf_drv_pwm_ipwm_config(pwm_config);
    }
    else {
        snf_drv_pwm_spwm_config(pwm_config);
    }
}

/**
 * @brief 配置IPWM
 *
 * @param pwm_config  PWM配置结构体
 */
static void snf_drv_pwm_ipwm_config(const snf_pwm_config_t *pwm_config)
{
    uint8_t  clk_psc;
    uint16_t mod_value;
    if (pwm_config->freq == PWM_FREQ_20KHZ) {
        clk_psc   = 15U - 1U;
        mod_value = 100U - 1U;
    }
    else {
        clk_psc   = 60U - 1U;
        mod_value = 25000U - 1U;
    }

    ipwm_combine_ch_config_t channel_config[pwm_config->channel_num];
    for (uint8_t i = 0; i < pwm_config->channel_num; ++i) {
        channel_config[i].channel                       = (ipwm_channel_t)pwm_config->channel_list[i];
        channel_config[i].counterConfig.localClkPsc     = clk_psc;
        channel_config[i].counterConfig.cntEn           = true;
        channel_config[i].counterConfig.initValue       = 1U;
        channel_config[i].counterConfig.modValue        = mod_value;
        channel_config[i].counterConfig.overflowIntEn   = false;
        channel_config[i].counterConfig.underflowIntEn  = false;
        channel_config[i].counterConfig.cntOverflowFreq = 0U;
        channel_config[i].mode                          = IPWM_COMBINE_OUTPUT_CONTINOUS_MODE;
        channel_config[i].countMode                     = IPWM_UP_COUNT;
        channel_config[i].cva                           = 1U; /*!< Channel match value a */
        channel_config[i].cvb                           = 1U; /*!< Channel match value b */
        channel_config[i].minTriggerEn                  = false;
        channel_config[i].maxTriggerEn                  = false;
        channel_config[i].eventaLevelMode               = IPWM_OUTPUT_HIGH_TRUE; /*!< Event a Output mode */
        channel_config[i].cvaMatchDir                   = IPWM_MATCH_DIR_UP;     /*!< CVa match dir */
        channel_config[i].cvbMatchDir                   = IPWM_MATCH_DIR_UP;     /*!< CVb match dir */
        channel_config[i].chPolarity   = IPWM_OUTPUT_POLARITY_ACTIVE_HIGH; /*!< Channel active polarity */
        channel_config[i].initOutputEn = true;           /*!< Enable/Disable Initial Output */
        channel_config[i].initLevel    = IPWM_LOW_LEVEL; /*!< Channel init output level */
        channel_config[i].cvaMatchTriggerEn = false;     /*!< Enable/Disable CVa match trigger */
        channel_config[i].cvbMatchTriggerEn = false;     /*!< Enable/Disable CVb match trigger */
        channel_config[i].eventaIntEn       = false;     /*!< Enable/Disable channel event a interrupt */
        channel_config[i].eventaFreq        = IPWM_CH_EVENT_RATIO_1; /*!< CHFa Interrupt ratio */
        channel_config[i].eventbIntEn       = false; /*!< Enable/Disable channel event b interrupt */
        channel_config[i].eventbFreq        = IPWM_CH_EVENT_RATIO_1; /*!< CHFb Interrupt ratio */
    }

    ipwm_combine_output_config_t combine_config = {
        .channelNum       = pwm_config->channel_num,
        .globalPscVal     = 1U,   /*!< Global clk psc value */
        .globalPscEn      = true, /* TODO Global clk psc enable */
        .combineChConfig  = channel_config,
        .triggerRatio     = IPWM_TRIGGER_RATIO_1,
        .intEn            = false,
        .channelCallBack  = NULL,
        .overflowCallBack = NULL,
    };

    IPWM_InitCombineOutput(pwm_config->instance - 3, &combine_config);
}

/**
 * @brief 配置SWPM
 *
 * @param pwm_config  PWM配置结构体
 */
static void snf_drv_pwm_spwm_config(const snf_pwm_config_t *pwm_config)
{
    spwm_independent_ch_config_t independent_ch_config[pwm_config->channel_num];
    for (uint8_t i = 0; i < pwm_config->channel_num; ++i) {
        independent_ch_config[i].channel        = (spwm_channel_t)pwm_config->channel_list[i];
        independent_ch_config[i].chValue        = 0U;
        independent_ch_config[i].levelMode      = SPWM_OUTPUT_HIGH_TRUE;
        independent_ch_config[i].polarity       = SPWM_OUTPUT_POLARITY_ACTIVE_HIGH;
        independent_ch_config[i].initLevel      = SPWM_LOW_LEVEL;
        independent_ch_config[i].matchTriggerEn = false;
        independent_ch_config[i].eventRatio     = SPWM_CH_EVENT_RATIO_1;
        independent_ch_config[i].chIntEn        = false;
    }

    uint8_t  clk_psc;
    uint16_t mod_value;
    if (pwm_config->freq == PWM_FREQ_20KHZ) {
        clk_psc   = 30U - 1U;
        mod_value = 100U - 1U;
    }
    else {
        clk_psc   = 60U - 1U;
        mod_value = 10000U - 1U;
    }

    spwm_independent_output_config_t independent_config = {
        .counterConfig = {
            .clkPsc = clk_psc,                      
            .cntEn = true,                     
            .initValue = 1U,                   
            .modValue = mod_value,                   
            .overflowIntEn = false,             
            .underflowIntEn = false,           
            .cntOverflowFreq = 0U,             
        },
        .countMode = SPWM_UP_COUNT,            
        .channelNum = pwm_config->channel_num,                      
        .independentChConfig = independent_ch_config,   
        .initOutputEn = true,                   
        .minTriggerEn = false,                 
        .maxTriggerEn = false,                 
        .triggerRatio = SPWM_TRIGGER_RATIO_1, 
        .intEn = false,                         
        .channelCallBack = NULL,                       
        .overflowCallBack = NULL,                     
    };

    SPWM_InitIndependentOutput(pwm_config->instance, &independent_config);
}

/**
 * @brief GPIO配置
 *
 * @param pwm_config   PWM配置结构体
 */
static void snf_drv_pwm_gpio_config(const snf_pwm_config_t *pwm_config)
{
    for (uint8_t i = 0; i < pwm_config->channel_num; ++i) {
        GPIO_Type *port = port_table[pwm_config->pin_num_list[i] / PORT_PIN_MAX_SIZE];
        uint32_t   pin  = pwm_config->pin_num_list[i] % PORT_PIN_MAX_SIZE;

        /* 计算hash_key 获取端口mux */
        uint32_t hash_key;
        if (pwm_config->instance >= PWM_INSTANCE_IPWM0) {
            hash_key = CAL_HASH_KEY_IPWM(pwm_config->pin_num_list[i], pwm_config->channel_list[i], pin);
        }
        else {
            hash_key = CAL_HASH_KEY_SPWM(pwm_config->pin_num_list[i], pwm_config->channel_list[i], pin);
        }
        gpio_pin_mux_t mux = snf_drv_pwm_get_hash_valve(hash_key);

        GPIO_SetPinFunc(port, pin, mux);
    }
}

/**
 * @brief 根据KEY获取HASH值
 *
 * @param hash_key          KEY值
 * @return gpio_pin_mux_t   HASH值
 */
static gpio_pin_mux_t snf_drv_pwm_get_hash_valve(uint32_t hash_key)
{
    for (uint8_t i = 0; i < HASH_TABLE_MAXSIZE; ++i) {
        if (mux_hash_table[i].key == hash_key) {
            return mux_hash_table[i].value;
        }
    }

    return GPIO_PIN_MUX_DISABLED;
}

/**
 * @brief 校验参数
 *
 * @param pwm_config     PWM配置结构体
 * @return result_e      校验结果
 */
static result_e snf_drv_pwm_param_verify(const snf_pwm_config_t *pwm_config)
{
    if (!pwm_config) {
        return RESULT_STATUS_INVALID_PARAM;
    }

    if (pwm_config->instance >= PWM_INSTANCE_MAX) {
        return RESULT_STATUS_INVALID_PARAM;
    }

    if (pwm_config->channel_num > PWM_CHANNEL_MAX) {
        return RESULT_STATUS_INVALID_PARAM;
    }

    for (uint8_t i = 0; i < pwm_config->channel_num; ++i) {
        if (pwm_config->channel_list[i] >= PWM_CHANNEL_MAX) {
            return RESULT_STATUS_INVALID_PARAM;
        }

        if (pwm_config->pin_num_list[i] >= GPIO_MAX_PIN_NUM) {
            return RESULT_STATUS_INVALID_PARAM;
        }
    }

    return RESULT_STATUS_OK;
}
